unit opentempus;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, FileUtil, LResources, Forms, Controls, Graphics, Dialogs,
  StdCtrls, ExtCtrls, Buttons, Menus, ComCtrls, sqlite3ds, configuration, apiplatforms,
  PopupNotifier, slimtimer, otlog, XMLPropStorage, spreadsheet, googledocs, utils,
  ExtDlgs, rules, Process, report, ShellApi, ColorBox, CheckLst;

const

   SecondsDay: integer = 86400;

type

  TAPIDBR = array of TStringList;

  { TMain }

  TMain = class(TForm)
    ApplicationProperties1: TApplicationProperties;
    Button1: TButton;
    ButtonEditRule: TButton;
    ButtonRunReport: TButton;
    ButtonCalendar1: TButton;
    Button3: TButton;
    ButtonDeleteRule: TButton;
    ButtonOrderRulesDown: TButton;
    ButtonAddCategory: TButton;
    ButtonOrderRulesUp: TButton;
    ButtonSaveRule: TButton;
    ButtonEditCategory: TButton;
    ButtonDeleteCategory: TButton;
    ButtonDeleteTask: TButton;
    Button8: TButton;
    ButtonAddTask: TButton;
    ButtonSave: TButton;
    ButtonCancel: TButton;
    CalendarDialog1: TCalendarDialog;
    ListBoxStorage: TCheckListBox;
    CheckStorageGoogleDocs: TCheckBox;
    CheckStorageSpreadsheet: TCheckBox;
    CheckStorageSlimTimer: TCheckBox;
    CheckGroup1: TCheckGroup;
    CheckPositiveFilters: TCheckBox;
    CheckNegativeFilters: TCheckBox;
    CheckRunStartup: TCheckBox;
    CheckNoSaveWindows: TCheckBox;
    CheckNoSaveProcess: TCheckBox;
    CheckActivateLoging: TCheckBox;
    ComboBox1: TComboBox;
    ComboNewRuleLogic: TComboBox;
    ComboTaskCategory: TComboBox;
    ComboGoogleDocsStorageFreq: TComboBox;
    ComboGoogleDocsWorksheet: TComboBox;
    ComboGoogleDocsSpreadsheet: TComboBox;
    ComboGoogleDocsRecover: TComboBox;
    ComboSlimTimerRecover: TComboBox;
    ComboSpreadsheetRecover: TComboBox;
    ComboSpreadSheetStorageFreq: TComboBox;
    ComboSpreadsheetPeriod: TComboBox;
    ComboSpreadsheetFormat: TComboBox;
    ComboMinTrackingSeconds: TComboBox;
    ComboSlimTimerUpdateFrequency: TComboBox;
    ComboIdleStatus: TComboBox;
    ComboUpdateFrequency: TComboBox;
    ComboTrackingInterval: TComboBox;
    ComboNotifyIdle: TComboBox;
    EditDate2: TEdit;
    EditDate1: TEdit;
    EditNewRuleProcess: TEdit;
    EditNewRuleWindow: TEdit;
    EditNewRuleName: TEdit;
    EditNewCategory: TEdit;
    EditTaskName: TEdit;
    EditGoogleDocsUsername: TEdit;
    EditGoogleDocsPassword: TEdit;
    EditSpreadSheetDirPath: TEdit;
    EditSlimTimerAPIKey: TEdit;
    EditSlimTimerUsername: TEdit;
    EditSlimTimerPassword: TEdit;
    EditFilterTrackWindows: TEdit;
    EditFilterTrackProcesses: TEdit;
    EditFilterNoTrackWindows: TEdit;
    EditFilterNoTrackProcesses: TEdit;
    GroupBox1: TGroupBox;
    GroupBox10: TGroupBox;
    GroupBox11: TGroupBox;
    GroupBox2: TGroupBox;
    GroupBox3: TGroupBox;
    GroupBox4: TGroupBox;
    GroupBox5: TGroupBox;
    GroupBox6: TGroupBox;
    GroupBox7: TGroupBox;
    GroupBox8: TGroupBox;
    GroupBox9: TGroupBox;
    Label1: TLabel;
    Label10: TLabel;
    Label11: TLabel;
    Label12: TLabel;
    Label13: TLabel;
    Label14: TLabel;
    Label15: TLabel;
    Label16: TLabel;
    Label17: TLabel;
    Label18: TLabel;
    Label19: TLabel;
    Label2: TLabel;
    Label20: TLabel;
    Label21: TLabel;
    Label22: TLabel;
    Label23: TLabel;
    Label24: TLabel;
    Label25: TLabel;
    Label26: TLabel;
    Label27: TLabel;
    Label28: TLabel;
    Label29: TLabel;
    Label3: TLabel;
    Label30: TLabel;
    Label31: TLabel;
    Label32: TLabel;
    Label33: TLabel;
    Label34: TLabel;
    Label35: TLabel;
    Label36: TLabel;
    Label37: TLabel;
    LabelReportName: TLabel;
    Label4: TLabel;
    Label5: TLabel;
    Label6: TLabel;
    Label7: TLabel;
    Label8: TLabel;
    Label9: TLabel;
    ListBoxReports: TListBox;
    ListBoxRules: TListBox;
    MemoReportSummary: TMemo;
    MenuItem1: TMenuItem;
    MenuItem2: TMenuItem;
    MenuItem3: TMenuItem;
    MenuItem4: TMenuItem;
    MenuItem5: TMenuItem;
    MenuItem6: TMenuItem;
    PageControl1: TPageControl;
    PageControl2: TPageControl;
    PageControl3: TPageControl;
    PageControl4: TPageControl;
    PopupMenu1: TPopupMenu;
    PopupNotifier1: TPopupNotifier;
    SelectDirectoryDialog1: TSelectDirectoryDialog;
    Sqlite3Dataset1: TSqlite3Dataset;
    StaticText1: TStaticText;
    TabSheet1: TTabSheet;
    TabSheet10: TTabSheet;
    TabSheet11: TTabSheet;
    TabSheet12: TTabSheet;
    TabSheet13: TTabSheet;
    TabSheet14: TTabSheet;
    TabSheet2: TTabSheet;
    TabSheet3: TTabSheet;
    TabSheet4: TTabSheet;
    TabSheet5: TTabSheet;
    TabSheet6: TTabSheet;
    TabSheet7: TTabSheet;
    TabSheet8: TTabSheet;
    TabSheet9: TTabSheet;
    Timer1: TTimer;
    TrayIcon1: TTrayIcon;
    TreeTasksRules: TTreeView;
    TreeCatTask: TTreeView;
    TreeTasks: TTreeView;
    XMLPropStorage1: TXMLPropStorage;

    procedure ApplicationProperties1Minimize(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure Button3Click(Sender: TObject);
    procedure ButtonEditCategoryClick(Sender: TObject);
    procedure ButtonCalendar1Click(Sender: TObject);
    procedure ButtonDeleteCategoryClick(Sender: TObject);
    procedure ButtonDeleteRuleClick(Sender: TObject);
    procedure ButtonDeleteTaskClick(Sender: TObject);
    procedure Button8Click(Sender: TObject);
    procedure ButtonAddCategoryClick(Sender: TObject);
    procedure ButtonEditRuleClick(Sender: TObject);
    procedure ButtonOrderRulesDownClick(Sender: TObject);
    procedure ButtonOrderRulesUpClick(Sender: TObject);
    procedure ButtonRunReportClick(Sender: TObject);
    procedure ButtonSaveRuleClick(Sender: TObject);
    procedure ButtonAddTaskClick(Sender: TObject);
    procedure ButtonCancelClick(Sender: TObject);
    procedure ButtonSaveClick(Sender: TObject);
    procedure ButtonTaskAddCategoryClick(Sender: TObject);
    procedure ListBoxStorageClickCheck(Sender: TObject);
    procedure CheckStorageGoogleDocsClick(Sender: TObject);
    procedure CheckNegativeFiltersChange(Sender: TObject);
    procedure CheckNoSaveProcessChange(Sender: TObject);
    procedure CheckNoSaveWindowsChange(Sender: TObject);
    procedure CheckPositiveFiltersChange(Sender: TObject);
    procedure CheckStorageSlimTimerClick(Sender: TObject);
    procedure CheckStorageSpreadsheetClick(Sender: TObject);
    procedure ColorListBox1Click(Sender: TObject);
    procedure ComboGoogleDocsSpreadsheetChange(Sender: TObject);
    procedure EditGoogleDocsPasswordChange(Sender: TObject);
    procedure FormCloseQuery(Sender: TObject; var CanClose: boolean);
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure FormShow(Sender: TObject);
    procedure ListBoxReportsClick(Sender: TObject);
    procedure MenuItem1Click(Sender: TObject);
    procedure MenuItem3Click(Sender: TObject);
    procedure MenuItem4Click(Sender: TObject);
    procedure MenuItem5Click(Sender: TObject);
    procedure MenuItem6Click(Sender: TObject);
    procedure PageControl1Change(Sender: TObject);
    procedure PageControl3Change(Sender: TObject);
    procedure TabSheet10Show(Sender: TObject);
    procedure TabSheet12ContextPopup(Sender: TObject; MousePos: TPoint;
      var Handled: Boolean);
    procedure TabSheet12Show(Sender: TObject);
    procedure TabSheet13Show(Sender: TObject);
    procedure TabSheet14Show(Sender: TObject);
    procedure TabSheet6Show(Sender: TObject);
    procedure TabSheet7Show(Sender: TObject);
    procedure TabSheet9Show(Sender: TObject);


    procedure Timer1Timer(Sender: TObject);
    procedure SavePreferences;
    procedure SetPreferences;
    procedure LoadRules;
    procedure LoadListRules;
    procedure LoadTaskTree;
    procedure LoadComboTaskCategory;
    procedure RecoverTracking(tracking: string);
    procedure TreeNewTaskChange(Sender: TObject; Node: TTreeNode);
    procedure XMLPropStorage1SavingProperties(Sender: TObject);


  private
    { private declarations }
    CFG: TOTConfig;
    UserIsIdle: boolean;
    {$IFDEF Windows}
    OTAPI: TWindowsPlatform;
    {$endif}
    PSlimTimer: TSlimTimer;
    PStorageSpreadSheet: TStorageSpreadSheet;
    PStorageGoogleDocs: TStorageGoogleDocs;
    CountTimerSeconds: integer;
    CountSlimTimerSeconds: integer;
    CountSpreadSheetStorageFreq: integer;
    CountGoogleDocsStorageFreq: integer;
    Log: TOTLog;
    Rules: TList;
    LetClose: boolean;
    PrevStorageSlimTimer: boolean;
    PrevStorageSpreadsheet: boolean;
    PrevStorageGoogleDocs: boolean;
    EditingTask: integer;
    EditingCategory: integer;
    EditingRule: integer;
  public
    { public declarations }
  end;

var
  Main: TMain;

implementation

{$IFDEF Windows}
uses Windows;
{$endif}
{$IFDEF Darwin}
uses
{$ifdef ver2_2_0}
  FPCMacOSAll;
{$else}
  MacOSAll;
{$ENDIF}
{$ENDIF}

{ TMain }

procedure TMain.FormCreate(Sender: TObject);
begin

  CFG := TOTConfig.Create;
  XMLPropStorage1.FileName := CFG.FilePath;

  Log := TOTLog.Create(CFG,TRUE);

  {$IFDEF Windows}
  OTAPI:= TWindowsPlatform.Create(CFG, Sqlite3Dataset1, Log, Rules);
  Sqlite3Dataset1.fileName := ExtractFilePath(Application.EXEName) + 'opentempus.s3db';
  {$endif}

  IF FileExists(XMLPropStorage1.FileName) THEN BEGIN
    XMLPropStorage1.Restore;
  END
  ELSE BEGIN
   CFG.Mode := ModeNormal;
  END;

  SetPreferences;

  LoadRules;

  LetClose := FALSE;

  UserIsIdle := FALSE;
  TrayIcon1.Visible := True;
  Timer1.Interval := 1000;
  CountTimerSeconds:=0;
  CountSlimTimerSeconds:=0;
  CountSpreadSheetStorageFreq := 0;
  CountGoogleDocsStorageFreq := 0;
  EditingTask := 0;
  EditingRule := 0;
  EditingCategory := 0;

  Timer1.Enabled := TRUE;

  PageControl1.ActivePageIndex := 0;

  Log.add('Starting Lazarus');

end;

procedure TMain.SetPreferences;
var
 i: integer;
begin
  IF FileExists(XMLPropStorage1.FileName) THEN
   CFG.Mode := strtoint(XMLPropStorage1.StoredValue['Mode']);

  IF CFG.Mode = ModeNormal THEN
   TrayIcon1.Icon.LoadFromFile(CFG.IconPath+'opentempus.ico')
  ELSE IF CFG.Mode = ModeTrackingDisabled THEN
   TrayIcon1.Icon.LoadFromFile(CFG.IconPath+'opentempustdisabled.ico')
  ELSE IF CFG.Mode = ModeFiltersDisabled THEN
   TrayIcon1.Icon.LoadFromFile(CFG.IconPath+'opentempusfdisabled.ico');

  for i:= 0 to main.ComponentCount -1 do
   CFG.SetPreference(Main.Components[i]);
end;

procedure TMain.SavePreferences;
begin
 XMLPropStorage1.StoredValue['Mode'] := inttostr(CFG.Mode);
 XMLPropStorage1.Save;
 SetPreferences;
end;

procedure TMain.RecoverTracking(tracking: string);
var
 tcfg, sqlcondition: string;
 ltime: integer;
begin
 tcfg := CFG.Get(tracking+'Recover');
 sqlcondition := '';

 IF tcfg <> 'None' THEN BEGIN
   IF tcfg = 'Last Day' THEN BEGIN
    ltime := UnixTime() - SecondsDay;
    sqlcondition := 'AND timestart > '+inttostr(ltime);
   END
   ELSE IF tcfg = 'Last Week' THEN BEGIN
    ltime := UnixTime() - (7 * SecondsDay);
    sqlcondition := 'AND timestart > '+inttostr(ltime);
   END
   ELSE IF tcfg = 'Last Month' THEN BEGIN
    ltime := UnixTime() - (30 * SecondsDay);
    sqlcondition := 'AND timestart > '+inttostr(ltime);
   END
   ELSE IF tcfg = 'Last 3 Month' THEN BEGIN
    ltime := UnixTime() - (91 * SecondsDay);
    sqlcondition := 'AND timestart > '+inttostr(ltime);
   END
   ELSE IF tcfg = 'Last 6 Month' THEN BEGIN
    ltime := UnixTime() - (182 * SecondsDay);
    sqlcondition := 'AND timestart > '+inttostr(ltime);
   END
   ELSE IF tcfg = 'Last Year' THEN BEGIN
    ltime := UnixTime() - (365 * SecondsDay);
    sqlcondition := 'AND timestart > '+inttostr(ltime);
   END;

   Log.add('Recovering tracking: SQL Query '+sqlcondition);
   OTAPI.SQLExec('UPDATE storedata_wpt SET save'+LowerCase(tracking)+' = 0 WHERE save'+LowerCase(tracking)+' = -1 '+sqlcondition);
 END
end;





{This is not working fine on Lazarus, this is the reason for the spaghetti code
 See http://www.mail-archive.com/lazarus@lazarus.freepascal.org/msg03056.html for more info
}
procedure TMain.ApplicationProperties1Minimize(Sender: TObject);
var
  {$IFDEF Windows}
   OwnerWnd : HWnd;
  {$endif}
begin
  {$IFDEF Windows}
  OwnerWnd:=GetWindow(Handle,GW_OWNER);
  ShowWindow(OwnerWnd,SW_HIDE);
  {$endif}
  Main.Visible := False;
  Main.Hide;
  Application.ShowMainForm := False;
  Application.ProcessMessages;
end;

procedure TMain.Button1Click(Sender: TObject);
begin
  IF SelectDirectoryDialog1.Execute THEN
   EditSpreadSheetDirPath.text := SelectDirectoryDialog1.FileName;
end;

procedure TMain.Button3Click(Sender: TObject);
begin
  if CalendarDialog1.Execute then
  EditDate2.Text:=DateTimeToStr(CalendarDialog1.Date);
end;

procedure TMain.ButtonEditCategoryClick(Sender: TObject);
begin
  EditingCategory := integer(TreeCatTask.Selected.Data);
  ButtonAddCategory.Caption:='Update';
  EditNewCategory.Text:= TreeCatTask.selected.Text;
end;

procedure TMain.ButtonCalendar1Click(Sender: TObject);
begin
  if CalendarDialog1.Execute then
  EditDate1.Text:=DateTimeToStr(CalendarDialog1.Date);
end;

procedure TMain.ButtonDeleteCategoryClick(Sender: TObject);
var
 idc: integer;
 buttonSelected : Integer;
begin
  idc := integer(TreeCatTask.Selected.Data);
  IF idc > 1 THEN BEGIN
   buttonSelected := MessageDlg('Are you sure you want to delete this category?\n Tasks will be move to Miscellaneous category',mtError, mbOKCancel, 0);
   if buttonSelected = mrOK  then BEGIN
    OTAPI.SQLExec('UPDATE task SET idc = "1" WHERE idc = "'+IntToStr(idc)+'"');
    OTAPI.SQLExec('DELETE FROM category WHERE id = "'+IntToStr(idc)+'"');
    LoadTaskTree
   END
  END
end;

procedure TMain.ButtonDeleteRuleClick(Sender: TObject);
var
 i: integer;
 sql: string;
begin
  FOR i:= 0 TO ListBoxRules.Count -1 DO BEGIN
   If ListBoxRules.Selected[i] THEN BEGIN
     OTAPI.SQLExec('DELETE FROM rules WHERE id = "'+IntToStr(integer(ListBoxRules.Items.Objects[i]))+'"');
     sql := 'UPDATE rules SET `order` = `order` - 1 WHERE `order` > '+IntToStr(i + 1);
     OTAPI.SQLExec(sql);
    END
  END;
  LoadListRules;
end;

procedure TMain.ButtonDeleteTaskClick(Sender: TObject);
var
 idt: integer;
 buttonSelected : Integer;
begin
  idt := integer(TreeTasks.Selected.Data);
  IF idt > 0 THEN BEGIN
   buttonSelected := MessageDlg('Are you sure you want to delete this task?',mtError, mbOKCancel, 0);
   if buttonSelected = mrOK  then BEGIN
    OTAPI.SQLExec('DELETE FROM task WHERE id = "'+IntToStr(idt)+'"');
    LoadTaskTree
   END
  END
end;


procedure TMain.ButtonOrderRulesUpClick(Sender: TObject);
var
 i, sel: integer;
begin
  FOR i:= 1 TO ListBoxRules.Count -1 DO BEGIN
   If ListBoxRules.Selected[i] THEN BEGIN
     OTAPI.SQLExec('UPDATE rules SET `order` = "'+IntToStr(i + 1)+'" WHERE `order` = "'+IntToStr(i)+'"');
     OTAPI.SQLExec('UPDATE rules SET `order` = "'+IntToStr(i)+'" WHERE id = "'+IntToStr(integer(ListBoxRules.Items.Objects[i]))+'"');
     sel := i - 1;
   END
  END;
  LoadListRules;
  ListBoxRules.Selected[sel] := TRUE;
end;

procedure TMain.ButtonRunReportClick(Sender: TObject);
var
 AProcess: TProcess;
 cmline: string;
 Report: TReport;
 i: integer;
begin

 IF ListBoxReports.Items.Count > 0 THEN
  FOR i:= 0 TO ListBoxReports.Items.Count -1 DO BEGIN
   IF ListBoxReports.Selected[i] THEN BEGIN
     Report := TReport.Create(CFG.ReportsPath+'/'+ListBoxReports.Items[i]+'/config.xml', CFG);
     cmline := Report.CmdLine(EditDate1.Text,EditDate2.Text);
     MemoReportSummary.Lines.Add(cmline);
     AProcess := TProcess.Create(nil);
     AProcess.CommandLine := cmline;
     AProcess.Options := AProcess.Options + [poWaitOnExit];
     AProcess.ShowWindow := swoHIDE;
     AProcess.Execute;
     AProcess.Free;

     {$IFDEF Windows}
     ShellExecute(Main.Handle,nil,PChar(Report.DirPath+Report.Output),'','',SW_SHOWNORMAL);
     {$endif}
   END;
  END;

end;


procedure TMain.TreeNewTaskChange(Sender: TObject; Node: TTreeNode);
begin
  //showmessage(Node.Text+inttostr(integer(Node.data)));
end;

procedure TMain.XMLPropStorage1SavingProperties(Sender: TObject);
begin

end;



procedure TMain.ButtonCancelClick(Sender: TObject);

begin
  // Force the main form to restore the original preferences
  XMLPropStorage1.Restore;
  Main.Hide;
end;

procedure TMain.ButtonSaveClick(Sender: TObject);
var
 RunStartup: boolean;
begin

  RunStartup := strtobool(CFG.Get('RunStartup'));
  // Save to the xml
  SavePreferences;
  // Load de CFG object with the new preferences
  SetPreferences;

  IF (NOT RunStartup) AND (strtobool(CFG.Get('RunStartup'))) THEN
     OTAPI.RunStartUp(TRUE);

  IF (RunStartup) AND (NOT strtobool(CFG.Get('RunStartup'))) THEN
     OTAPI.RunStartUp(FALSE);

  Main.hide;
  Log.add('Configuration saved');

  IF (NOT PrevStorageSpreadsheet) AND (strtobool(CFG.Get('StorageSpreadsheet'))) THEN
    RecoverTracking('Spreadsheet');

  IF (NOT PrevStorageSlimTimer) AND (strtobool(CFG.Get('StorageSlimTimer'))) THEN
    RecoverTracking('SlimTimer');

  IF (NOT PrevStorageGoogleDocs) AND (strtobool(CFG.Get('StorageGoogleDocs'))) THEN
    RecoverTracking('GoogleDocs');
end;

procedure TMain.Button8Click(Sender: TObject);
begin
 EditingTask:=integer(TreeTasks.Selected.Data);
 IF EditingTask > 0 THEN BEGIN
  EditTaskName.Text:= TreeTasks.selected.Text;
  ComboTaskCategory.Text:= TreeTasks.selected.Parent.Text;
  ButtonAddTask.Caption:='Update';
 END
end;

procedure TMain.ButtonTaskAddCategoryClick(Sender: TObject);
begin
  TabSheet10.Show;
end;

procedure TMain.ListBoxStorageClickCheck(Sender: TObject);
begin
  //showmessage(booltostr(ListBoxStorage.Checked[0]));
end;

procedure TMain.CheckStorageGoogleDocsClick(Sender: TObject);
begin
    PageControl2.TabIndex:=2;
end;


procedure TMain.CheckNegativeFiltersChange(Sender: TObject);
begin
  IF CheckNegativeFilters.Checked = TRUE THEN BEGIN
    CheckPositiveFilters.Checked := FALSE;

    EditFilterTrackProcesses.Enabled := FALSE;
    EditFilterTrackProcesses.Color := clInactiveBorder;
    EditFilterTrackWindows.Enabled := FALSE;
    EditFilterTrackWindows.Color := clInactiveBorder;

    EditFilterNoTrackProcesses.Enabled := TRUE;
    EditFilterNoTrackProcesses.color := clWindow;
    EditFilterNoTrackWindows.Enabled := TRUE;
    EditFilterNoTrackWindows.color := clWindow;
  END
  ELSE BEGIN
    EditFilterNoTrackProcesses.Enabled := FALSE;
    EditFilterNoTrackProcesses.color := clInactiveBorder;
    EditFilterNoTrackWindows.Enabled := FALSE;
    EditFilterNoTrackWindows.color := clInactiveBorder;
  END;
end;

procedure TMain.CheckNoSaveProcessChange(Sender: TObject);
begin
  IF CheckNoSaveProcess.Checked = TRUE THEN
   CheckNoSaveWindows.Checked := FALSE;
end;

procedure TMain.CheckNoSaveWindowsChange(Sender: TObject);
begin
  IF CheckNoSaveWindows.Checked = TRUE THEN
   CheckNoSaveProcess.Checked := FALSE;
end;

procedure TMain.CheckPositiveFiltersChange(Sender: TObject);
begin
  IF CheckPositiveFilters.Checked = TRUE THEN BEGIN
    CheckNegativeFilters.Checked := FALSE;

    EditFilterTrackProcesses.Enabled := TRUE;
    EditFilterTrackProcesses.color := clWindow;
    EditFilterTrackWindows.Enabled := TRUE;
    EditFilterTrackWindows.color := clWindow;

    EditFilterNoTrackProcesses.Enabled := FALSE;
    EditFilterNoTrackProcesses.Color := clInactiveBorder;
    EditFilterNoTrackWindows.Enabled := FALSE;
    EditFilterNoTrackWindows.Color := clInactiveBorder;
  END
  ELSE BEGIN
    EditFilterTrackProcesses.Enabled := FALSE;
    EditFilterTrackProcesses.color := clInactiveBorder;
    EditFilterTrackWindows.Enabled := FALSE;
    EditFilterTrackWindows.color := clInactiveBorder;
  END;
end;

procedure TMain.CheckStorageSlimTimerClick(Sender: TObject);
begin
  PageControl2.TabIndex:= 0;
end;

procedure TMain.CheckStorageSpreadsheetClick(Sender: TObject);
begin
  PageControl2.TabIndex:=1;
end;

procedure TMain.ColorListBox1Click(Sender: TObject);
begin

end;

procedure TMain.ComboGoogleDocsSpreadsheetChange(Sender: TObject);
var
 auxitems: tstringlist;
 i: integer;
begin
 auxitems:= tstringlist.Create;
 auxitems.Add('Each Year');
 auxitems.Add('Each Month');
 auxitems.Add('Each Week');
 auxitems.Add('Each Day');
 auxitems.Add('None');

 ComboGoogleDocsWorksheet.Clear;
 FOR i:= auxitems.IndexOf(ComboGoogleDocsSpreadsheet.Text) + 1 TO auxitems.Count -1 DO BEGIN
  ComboGoogleDocsWorksheet.items.Add(auxitems[i]);
 END;
 ComboGoogleDocsWorksheet.ItemIndex:=0;

end;

procedure TMain.EditGoogleDocsPasswordChange(Sender: TObject);
begin

end;

procedure TMain.FormCloseQuery(Sender: TObject; var CanClose: boolean);
begin
 XMLPropStorage1.Restore;
 Self.hide;
 IF LetClose THEN
  CanClose := TRUE
 ELSE
  CanClose := FALSE;
end;

procedure TMain.FormDestroy(Sender: TObject);
begin
  OTAPI.FinishCurrentWindowProcess;
  Log.add('Lazarus Closed');
end;

procedure TMain.FormShow(Sender: TObject);
begin
  PrevStorageSlimTimer:= CheckStorageSlimTimer.Checked;
  PrevStorageSpreadsheet:= CheckStorageSpreadsheet.Checked;
  PrevStorageGoogleDocs := CheckStorageGoogleDocs.Checked;
end;

procedure TMain.ListBoxReportsClick(Sender: TObject);

var
 i: integer;
 Report: TReport;
 path: string;
 mystr: TStrings;
begin

IF ListBoxReports.Items.Count > 0 THEN
  MemoReportSummary.Lines.Clear;
  FOR i:= 0 TO ListBoxReports.Items.Count -1 DO BEGIN
   IF ListBoxReports.Selected[i] THEN BEGIN
    Report := TReport.Create(CFG.ReportsPath+'/'+ListBoxReports.Items[i]+'/config.xml', CFG);
    MemoReportSummary.Lines.Add(Report.Summary);
    LabelReportName.Caption:='Title: '+Report.Name;
   END
  END
end;



procedure TMain.MenuItem1Click(Sender: TObject);
begin
  Main.Left := (Screen.Width DIV 2) - (Main.Width DIV 2);
  Main.Top := (Screen.Height DIV 2) - (Main.Height DIV 2);
  Main.Show;
end;

procedure TMain.MenuItem3Click(Sender: TObject);
begin
  CFG.Mode := ModeNormal;
  MenuItem3.Caption:= '• Normal';
  MenuItem4.Caption:= '  Tracking Disabled';
  MenuItem5.Caption:= '  Filters Disabled';
  TrayIcon1.Icon.LoadFromFile(CFG.IconPath+'opentempus.ico');
  SavePreferences;
  Log.add('Mode Normal Activated');
end;

procedure TMain.MenuItem4Click(Sender: TObject);
begin
  CFG.Mode := ModeTrackingDisabled;
  MenuItem3.Caption:= '  Normal';
  MenuItem4.Caption:= '• Tracking Disabled';
  MenuItem5.Caption:= '  Filters Disabled';
  TrayIcon1.Icon.LoadFromFile(CFG.IconPath+'opentempustdisabled.ico');
  OTAPI.FinishCurrentWindowProcess;
  SavePreferences;
  Log.add('Mode Tracking Disabled activated');
end;

procedure TMain.MenuItem5Click(Sender: TObject);
begin
  CFG.Mode := ModeFiltersDisabled;
  MenuItem3.Caption:= '  Normal';
  MenuItem4.Caption:= '  Tracking Disabled';
  MenuItem5.Caption:= '• Filters Disabled';
  TrayIcon1.Icon.LoadFromFile(CFG.IconPath+'opentempusfdisabled.ico');
  {This should be improved, not stop the current process, some better solution missing}
  OTAPI.FinishCurrentWindowProcess;
  SavePreferences;
  Log.add('Mode Filters Disabled activated');
end;

procedure TMain.MenuItem6Click(Sender: TObject);
begin
  LetClose := TRUE;
  Self.Close;
end;

procedure TMain.PageControl1Change(Sender: TObject);
begin

end;

procedure TMain.PageControl3Change(Sender: TObject);
begin

end;

procedure TMain.TabSheet10Show(Sender: TObject);
begin
  EditingCategory:=0;
  ButtonAddCategory.Caption:='Add';
end;

procedure TMain.TabSheet12ContextPopup(Sender: TObject; MousePos: TPoint;
  var Handled: Boolean);
begin

end;

procedure TMain.LoadListRules;
var
 i: integer;
 records: TAPIDBR;
 rt: tstringlist;

begin

 ListBoxRules.Items.Clear;

 records := OTAPI.SQLRecords('SELECT * FROM rules ORDER BY `order`');
 FOR i:= 0 TO Length(records) - 1 DO BEGIN
  rt := tstringlist(records[i]);
  ListBoxRules.Items.AddObject(IntToStr(i + 1)+' - '+rt.Values['name']+' ('+rt.Values['window']+' '+rt.Values['logic']+' '+rt.Values['process']+')',tobject(strtoint(rt.Values['id'])));
 END;

  LoadRules;
end;

procedure TMain.TabSheet12Show(Sender: TObject);
begin
 LoadListRules;
end;

procedure TMain.TabSheet13Show(Sender: TObject);
begin
  PageControl4.ActivePageIndex:=0;
  EditingRule:=0;
  ButtonSaveRule.Caption:='Add';
end;

procedure TMain.TabSheet14Show(Sender: TObject);
var
 searchResult : TSearchRec;

begin
 ListBoxStorage.Items.Clear;

 IF DirectoryExists(CFG.StoragePath) THEN BEGIN
  if FindFirst(CFG.StoragePath+'/*', faDirectory, searchResult) = 0 then
  begin
    repeat
      if (searchResult.attr and faDirectory) = faDirectory
      then BEGIN
        IF (searchResult.Name <> '.') AND (searchResult.Name <> '..') THEN BEGIN
          ListBoxStorage.Items.Add(searchResult.Name);
        END
      END

    until FindNext(searchResult) <> 0;

    FindCloseUTF8(searchResult);
  end;
 END

end;

procedure TMain.LoadRules;
var
 records: TAPIDBR;
 rt: tstringlist;
 i: integer;

begin
 Rules := TList.Create;

 records := OTAPI.SQLRecords('SELECT * FROM rules ORDER BY `order`');
 FOR i:= 0 TO Length(records) - 1 DO BEGIN
  rt := tstringlist(records[i]);
  Rules.Add(TRule.Create(rt.Values['window'],rt.Values['process'],rt.Values['logic'],strtoint(rt.Values['idt'])));
 END;

 OTAPI.Rules := Rules;
end;

procedure TMain.TabSheet6Show(Sender: TObject);
begin
 LoadTaskTree;
 PageControl3.ActivePageIndex:=0;
end;

procedure TMain.TabSheet7Show(Sender: TObject);
var
 searchResult : TSearchRec;
 Report: TReport;
 mystr: Tstrings;

begin
 ListBoxReports.Items.Clear;

 mystr := TStrings.Create;

 IF DirectoryExists(CFG.ReportsPath) THEN BEGIN
  if FindFirst(CFG.ReportsPath+'/*', faDirectory, searchResult) = 0 then
  begin
    repeat
      // Only show directories
      if (searchResult.attr and faDirectory) = faDirectory
      then BEGIN
        IF (searchResult.Name <> '.') AND (searchResult.Name <> '..') THEN BEGIN
          Report := TReport.Create(CFG.ReportsPath+'/'+searchResult.Name+'/config.xml', CFG);
          ListBoxReports.Items.Add(searchResult.Name);
          //mystr.Strings[0] := Report.Name;
          //ListBoxReports.Items.AddObject(Report.Name,TObject(mystr));
        END
      END

    until FindNext(searchResult) <> 0;

    FindCloseUTF8(searchResult);
  end;
 END
end;

procedure TMain.TabSheet9Show(Sender: TObject);
begin
 EditingTask := 0;
 ButtonAddTask.Caption:='Add';
end;

procedure TMain.LoadComboTaskCategory;
var
 records : TAPIDBR;
 rt: tstringlist;
 i: integer;
begin

  ComboTaskCategory.Clear;
  records := OTAPI.SQLRecords('SELECT * FROM category');
  FOR i:= 0 TO Length(records) - 1 DO BEGIN
   rt := tstringlist(records[i]);
   ComboTaskCategory.AddItem(rt.Values['name'], TObject(strtoint(rt.Values['id'])));
  END

end;


procedure TMain.ButtonAddTaskClick(Sender: TObject);
var
 node:ttreenode;
 idc: integer;
 sql: string;

begin
  //node := TreeNewTask.Items.AddChild(treenewtask.Selected,'ss');
  //node.data := pointer(45);
  //if TreeNewCategory.Selected <> nil then
  //  TreeNewCategory.Items.AddChild(TreeNewCategory.Selected ,s);

  idc := Integer(ComboTaskCategory.Items.Objects[ComboTaskCategory.Items.IndexOf(ComboTaskCategory.Text)]);

  IF idc <= 0 THEN BEGIN
   showmessage('Please, select a category')
  END
  ELSE IF EditTaskName.Text = '' THEN BEGIN
   showmessage('Please, write a task name')
  END
  ELSE BEGIN
   IF EditingTask > 0 THEN BEGIN
    sql := 'UPDATE task SET idc = '+Inttostr(idc)+', name = "'+EditTaskName.Text+'" WHERE id = '+IntToStr(EditingTask);
    EditingTask := 0;
    ButtonAddTask.Caption:='Add';
   END
   ELSE BEGIN
    sql := 'INSERT INTO task (idc,name) VALUES ("'+Inttostr(idc)+'", "'+EditTaskName.Text+'")';
   END;

   IF OTAPI.SQLExec(sql) THEN BEGIN
    LoadTaskTree;
    ComboTaskCategory.Text:='';
    EditTaskName.Text:='';
   END
  END
end;


procedure TMain.ButtonSaveRuleClick(Sender: TObject);
var
 idt, maxorder: integer;
 rt: tstringlist;
 records: TAPIDBR;
 sql: string;

begin
   // TODO Calendar - CalendarDialog1.Execute;

  idt := Integer(TreeTasksRules.Selected.Data);
  IF idt <> 0 THEN BEGIN

    IF EditingRule <> 0 THEN BEGIN
      sql := 'UPDATE rules SET name ="'+EditNewRuleName.Text+'", ';
      sql := sql +' window = "'+EditNewRuleWindow.Text+'", ';
      sql := sql + 'process = "'+EditNewRuleProcess.Text+'", logic = "'+ComboNewRuleLogic.Text+'", idt = "'+inttostr(idt)+'" WHERE id = '+inttostr(EditingRule);
      EditingRule:=0;
      ButtonSaveRule.Caption:='Save';
    END
    ELSE BEGIN
      records := OTAPI.SQLRecords('SELECT COUNT(id) as total, MAX(`order`) as maxorder FROM rules');
      IF(Length(records) = 0) THEN BEGIN
       maxorder := 1;
      END
      ELSE BEGIN
       rt := tstringlist(records[0]);
       maxorder := strtoint(rt.Values['maxorder']) + 1;
      END;
      sql := 'INSERT INTO rules (name, window, process, logic, idt, `order`) VALUES ("'+EditNewRuleName.Text+'","'+EditNewRuleWindow.Text+'","'+EditNewRuleProcess.Text+'", "'+ComboNewRuleLogic.Text+'", "'+inttostr(idt)+'", "'+inttostr(maxorder)+'")';
    END;
    IF OTAPI.SQLExec(sql) THEN BEGIN
     LoadRules;
     EditNewRuleName.Text:='';
     EditNewRuleWindow.Text:='';
     editnewruleprocess.Text:='';
    END
   END

  ELSE
      showmessage('Please, select a Task not a category');

end;

procedure TMain.ButtonAddCategoryClick(Sender: TObject);
var
 sql: string;

begin

 IF EditingCategory > 0 THEN BEGIN
  sql := 'UPDATE category SET  name = "'+EditNewCategory.Text+'" WHERE id = '+IntToStr(EditingCategory);
  EditingCategory:=0;
  ButtonAddCategory.Caption:='Add';
 END
 ELSE BEGIN
  sql := 'INSERT INTO category (name) VALUES ("'+EditNewCategory.Text+'")';
 END;

 //OTAPI.SQLExec(sql);
 IF OTAPI.SQLExec(sql) THEN BEGIN
  LoadTaskTree;
  EditNewCategory.Text:='';
 END
end;

procedure TMain.ButtonEditRuleClick(Sender: TObject);
var
 rt: tstringlist;
 records: TAPIDBR;
 i: integer;
begin

  FOR i:= 0 TO ListBoxRules.Count -1 DO BEGIN
   If ListBoxRules.Selected[i] THEN BEGIN
      EditingRule := integer(ListBoxRules.Items.Objects[i]);
      records := OTAPI.SQLRecords('SELECT * FROM rules WHERE id = "'+inttostr(EditingRule)+'"');
    END
  END;
 rt := records[0];
 PageControl4.TabIndex:=0;
 EditNewRuleName.Text:=rt.values['name'];
 EditNewRuleWindow.Text:=rt.values['window'];
 EditNewRuleProcess.Text:=rt.values['process'];
 ComboNewRuleLogic.Text:=rt.values['logic'];
 TreeTasksRules.selected := TreeTasksRules.Items.FindNodeWithData(tobject(strtoint(rt.values['idt'])));
 ButtonSaveRule.Caption:='Update';
end;

procedure TMain.ButtonOrderRulesDownClick(Sender: TObject);
  var
 i, sel: integer;
 sql: string;
begin
  FOR i:= 0 TO ListBoxRules.Count -2 DO BEGIN
   If ListBoxRules.Selected[i] THEN BEGIN
    sql := 'UPDATE rules SET `order` = "'+IntToStr(i + 1)+'" WHERE `order` = "'+IntToStr(i + 2)+'"';
    OTAPI.SQLExec(sql);

    sql := 'UPDATE rules SET `order` = "'+IntToStr(i + 2)+'" WHERE id = "'+IntToStr(integer(ListBoxRules.Items.Objects[i]))+'"';
    OTAPI.SQLExec(sql);
    sel := i + 1;
    END
  END;
  LoadListRules;
  ListBoxRules.selected[sel] := TRUE;
end;

procedure TMain.LoadTaskTree;
var
 node:ttreenode;
 categories : tstrings;
 i,j,cat : integer;
 records: TAPIDBR;
 rt: tstringlist;
begin

 LoadComboTaskCategory;

 TreeTasks.Items.Clear;
 TreeTasksRules.Items.Clear;
 TreeCatTask.Items.Clear;

 cat := 0;
 FOR i := 0 TO combotaskcategory.Items.count -1 do begin

     TreeTasks.Items.Add(nil,ComboTaskCategory.items[i]);
     TreeTasksRules.Items.Add(nil,ComboTaskCategory.items[i]);

     node := TreeCatTask.Items.Add(nil,ComboTaskCategory.items[i]);
     node.data := pointer(Integer(ComboTaskCategory.Items.Objects[i]));

     cat := TreeTasks.Items.count - 1;

     records := OTAPI.SQLRecords('SELECT * FROM task WHERE idc = "'+IntToStr(Integer(ComboTaskCategory.Items.Objects[i]))+'"');
     FOR j:= 0 TO Length(records) - 1 DO BEGIN
      rt := tstringlist(records[j]);
      node := TreeTasks.Items.AddChild(TreeTasks.Items[cat],rt.Values['name']);
      node.data := pointer(strtoint(rt.Values['id']));
      node := TreeTasksRules.Items.AddChild(TreeTasksRules.Items[cat],rt.Values['name']);
      node.data := pointer(strtoint(rt.Values['id']));
     END

     {Sqlite3Dataset1.SQL := 'SELECT * FROM task WHERE idc = "'+IntToStr(Integer(ComboTaskCategory.Items.Objects[i]))+'"';
     try
      Sqlite3Dataset1.Open;
      IF Sqlite3Dataset1.RecordCount > 0 THEN BEGIN
       Sqlite3Dataset1.first;
       WHILE NOT Sqlite3Dataset1.EOF DO BEGIN
        node := TreeTasks.Items.AddChild(TreeTasks.Items[cat],Sqlite3Dataset1.FieldByName('name').AsString);
        node.data := pointer(Sqlite3Dataset1.FieldByName('id').AsInteger);
        node := TreeTasksRules.Items.AddChild(TreeTasksRules.Items[cat],Sqlite3Dataset1.FieldByName('name').AsString);
        node.data := pointer(Sqlite3Dataset1.FieldByName('id').AsInteger);
        //node := TreeCatTask.Items.AddChild(TreeCatTask.Items[cat],Sqlite3Dataset1.FieldByName('name').AsString);
        //node.data := pointer(Sqlite3Dataset1.FieldByName('id').AsInteger);
        //ComboTaskCategory.AddItem(Sqlite3Dataset1.FieldByName('name').AsString, TObject(Sqlite3Dataset1.FieldByName('id').AsInteger));
        Sqlite3Dataset1.Next;
       END;
      END;
      finally
      Sqlite3Dataset1.close;
      end ;  }
 end;
end;

procedure TMain.Timer1Timer(Sender: TObject);
var
 IdleTimeLeft: integer;
 AuxSIdle: integer;

begin

 Inc(CountTimerSeconds);

 IF CFG.Mode = ModeTrackingDisabled THEN
  CountTimerSeconds := 0;

 IF strtobool(CFG.Get('StorageSlimTimer')) = TRUE THEN
    Inc(CountSlimTimerSeconds);

 IF strtobool(CFG.Get('StorageSpreadSheet')) = TRUE THEN
    Inc(CountSpreadSheetStorageFreq);

 IF strtobool(CFG.Get('StorageGoogleDocs')) = TRUE THEN
    Inc(CountGoogleDocsStorageFreq);

 IF CountTimerSeconds >= strtoint(CFG.Get('TrackingInterval')) THEN BEGIN

   CountTimerSeconds := 0;

   IF (CFG.Mode <> ModeTrackingDisabled) AND (strtoint(CFG.Get('NotifyIdle')) > 0) AND (PopupNotifier1.visible = FALSE) THEN BEGIN
    AuxSIdle := OTAPI.GetSecondsIdle;
    IF (AuxSIdle + strtoint(CFG.Get('NotifyIdle'))) > strtoint(CFG.Get('IdleStatus')) * 60 THEN BEGIN
      IdleTimeLeft := (strtoint(CFG.Get('IdleStatus')) * 60) - AuxSIdle;
      PopupNotifier1.Title:= 'openTempus Idle status';
      PopupNotifier1.Text:= wraptext('You are going to be Idle in '+IntToStr(IdletimeLeft)+' seconds \n your current task will be finished in openTempus',40);
      PopupNotifier1.ShowAtPos(Screen.Width - 100, Screen.Height - 50);

    END;
   END;

   IF (CFG.Mode <> ModeTrackingDisabled) AND (NOT UserIsIdle) THEN
   BEGIN
     IF OTAPI.GetSecondsIdle > strtoint(CFG.Get('IdleStatus')) * 60 THEN
     BEGIN
      OTAPI.FinishCurrentWindowProcess;
      UserIsIdle := TRUE;
      Log.add('User is idle');
     END
     ELSE BEGIN

      OTAPI.SetNewWindow;
      OTAPI.SetNewProcess;

      IF OTAPI.ChangedWindowProcess THEN
      BEGIN
       OTAPI.FinishCurrentWindowProcess;
       IF (CFG.Mode = ModeFiltersDisabled) OR (OTAPI.WindowProcessPassFilters) THEN
        OTAPI.SaveNewWindowProcess;
      END
      ELSE BEGIN
       OTAPI.UpdateCurrentWindowProcess;
      END;
     END;
   END;

   { Check Idle Status }
   IF (UserIsIdle) AND (CFG.Mode <> ModeTrackingDisabled) THEN
   BEGIN
    OTAPI.FinishCurrentWindowProcess;
    UserIsIdle := OTAPI.GetSecondsIdle > strtoint(CFG.Get('IdleStatus'));
   END
 END;

 IF (strtobool(CFG.Get('StorageSlimTimer')) = TRUE) AND (CountSlimTimerSeconds >= strtoint(CFG.Get('SlimTimerUpdateFrequency')) * 60) THEN BEGIN
  CountSlimTimerSeconds := 0;
  PSlimTimer := TSlimTimer.Create(TRUE, CFG, Sqlite3Dataset1, Log);
  PSlimTimer.Resume;
 END;

 IF (strtobool(CFG.Get('StorageSpreadSheet')) = TRUE) AND (CountSpreadSheetStorageFreq >= strtoint(CFG.Get('SpreadSheetStorageFreq')) * 60) THEN BEGIN
  CountSpreadSheetStorageFreq := 0;
  PStorageSpreadSheet := TStorageSpreadSheet.Create(TRUE,LOG,CFG,Sqlite3Dataset1);
  PStorageSpreadSheet.Resume;
 end;

 IF (strtobool(CFG.Get('StorageGoogleDocs')) = TRUE) AND (CountGoogleDocsStorageFreq >= strtoint(CFG.Get('GoogleDocsStorageFreq')) * 60) THEN BEGIN
  CountGoogleDocsStorageFreq := 0;
  PStorageGoogleDocs := TStorageGoogleDocs.Create(TRUE,LOG,CFG,Sqlite3Dataset1);
  PStorageGoogleDocs.Resume;
 end;

end;


initialization
  {$I opentempus.lrs}

end.

